package org.leiyang.netty.server;

import com.alibaba.fastjson.JSON;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.StringUtil;
import org.leiyang.common.dtos.Message;
import org.leiyang.common.dtos.MessageType;
import org.leiyang.common.util.IpUtils;
import org.leiyang.netty.listener.IClientChangeListener;
import org.leiyang.netty.listener.INettyMsgFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Objects;

//@Component
public class MsgServerHandler extends SimpleChannelInboundHandler<Object> {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final IClientChangeListener clientChangeListener;
    private final List<INettyMsgFilter> messageFilters;
    private final StringBuilder content = new StringBuilder();

//    @Value("${netty.port:18080}")
    private final int port;

//    @Value("${local.address}")
    private final String localAddress;

    public MsgServerHandler(IClientChangeListener clientChangeListener,
                            List<INettyMsgFilter> messageFilters, int port, String localAddress) {
        this.clientChangeListener = clientChangeListener;
        this.messageFilters = messageFilters;
        this.port = port;
        this.localAddress = localAddress;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        clientChangeListener.loseClient(ctx);
        ctx.close();
        super.channelInactive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        logger.error("服务端出现异常, ", cause);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception {
        //对服务端接收到的消息进行处理...
        if(Objects.isNull(msg)) {
            return;
        }
//        logger.info("当前netty服务端单次读取到的msg: {}", ((TextWebSocketFrame) msg).text());
        if(msg instanceof TextWebSocketFrame) {
            TextWebSocketFrame receiveMsg = (TextWebSocketFrame) msg;
            Message message = JSON.parseObject(receiveMsg.text(), Message.class);

            logger.info("当前netty服务端读取到的msg: {}是否属于Token校验类型: {}", message, message.getMessageType() == MessageType.TOKEN);
            Flux.fromIterable(messageFilters)
                .flatMap(messageFilter -> messageFilter.chain(message, channelHandlerContext, gotLocalIp(), String.valueOf(port), false))
                .filter(doNext -> !doNext)
                .take(1L)
                .subscribe(e -> logger.info("已执行符合要求的Filters的操作: {}", e));
        }
        //请求头
        else if (msg instanceof HttpRequest) {
//            HttpRequest req = (HttpRequest) msg;
//            QueryStringDecoder queryStringDecoder = new QueryStringDecoder(req.uri());
//            String token = queryStringDecoder.parameters().get(Constants.AUTHENTICATION).get(0);
            //表示是一个新的消息，所以重置content,清除之前的内容
            content.setLength(0);

        } else if (msg instanceof HttpContent){
            //请求体
            HttpContent httpContent = (HttpContent) msg;
            content.append(httpContent.content().toString(CharsetUtil.UTF_8));
            //消息结束，已获取完整的请求体内容
            if(httpContent instanceof LastHttpContent) {
                Message message = JSON.parseObject(content.toString(), Message.class);
                logger.info("当前netty服务端读取到完整的msg: {}", message);
                Mono<Boolean> meetAnyFilter = Flux.fromIterable(messageFilters)
                        .flatMap(messageFilter -> messageFilter.chain(message, channelHandlerContext, gotLocalIp(), String.valueOf(port), true))
                        .filter(doNext -> !doNext)
                        .hasElement(true);
                meetAnyFilter.subscribe();
            }
        }
    }

    /**
     * 获取本机IP
     * @return 本机IP
     */
    private String gotLocalIp() {
        return StringUtil.isNullOrEmpty(localAddress) ? IpUtils.getIp() : localAddress;
    }
}